package com.financing.service.invest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.financing.common.CONST;
import com.financing.components.payment.Gateway;
import com.financing.controller.invest.TradeController;
import com.financing.enums.finance.FinanceLogType;
import com.financing.enums.invest.BidStatus;
import com.financing.enums.invest.CouponRule;
import com.financing.enums.invest.InvestLogType;
import com.financing.enums.invest.RepayType;
import com.financing.enums.notify.AgentNotify;
import com.financing.enums.notify.AssetsNotify;
import com.financing.model.invest.Bid;
import com.financing.model.invest.BidCheck;
import com.financing.model.invest.CouponLog;
import com.financing.model.invest.Repay;
import com.financing.model.invest.ShareBenefit;
import com.financing.model.invest.ShareBenefitTemplet;
import com.financing.model.invest.Trade;
import com.financing.model.user.User;
import com.financing.service.BaseService;
import com.financing.service.finance.FinanceLogService;
import com.financing.service.system.AssetsLogService;
import com.financing.service.system.LoggerService;
import com.financing.service.user.UserRefereeService;
import com.financing.util.DateUtil;
import com.financing.util.Money;
import com.financing.util.Str;
import com.financing.util.XmlHelper;
import com.financing.util.invest.AverageCapitalPlusInterestUtils;
import com.financing.util.invest.AverageCapitalUtils;
import com.jfinal.kit.Ret;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.Record;

public class TradeService extends BaseService<Bid>{
	private static TradeService trade = new TradeService();
	private static LoggerService log = new LoggerService();
	private static CouponService coupon = new CouponService();
	private static BidCheckService bidCheck = new BidCheckService();	
	private static AssetsLogService assetsLog = new AssetsLogService();	
	private static UserRefereeService userReferee = new UserRefereeService();	
    private static final Logger logger = Logger.getLogger(TradeController.class);
 
    public synchronized static Ret run(User u, int bid, int tzhbId, int jxqId,int tyjId, int unit) {
		if(u.getIsBindBankCard()==1) {
			Bid b = Bid.me.findById(bid);
			String tradeCode = "";
	    	try{
	    		boolean isSuccess = false;
	    		double amount = Money.mul(b.getUnitAmount(), Double.valueOf(unit));
	    		Date now = new Date(); 
	    		double tradeAmount = 0.0;
	
	    		logger.error("购买用户==="+u.getRealName());
	    		logger.error("名称==="+b.getName());
	    		logger.error("状态==="+b.getStatus());
	    		logger.error("这次购买==="+unit);
	    		logger.error("已购买==="+b.getApplyUnit());
	    		logger.error("续增==="+b.getVirtualUnit());
	    		logger.error("总份数==="+b.getUnit());
	
	    		// 判断标的时间范围
	    		if(!DateUtil.isBetween(b.getStartTime(), b.getEndTime(), now)){
	    			return Ret.fail("msg", "不在标的可投时间范围内！");
	    		}
	    		
	    		// 判断标的项目状态
	    		if(!Str.eq(BidStatus.TRADING, b.getStatus())){
	    			return Ret.fail("msg", "标的状态不是募集状态，不可投资！");
	    		}
	    		
	    		// 判断可购买份数
	    		if(unit>= b.getUnit()-b.getApplyUnit()-b.getVirtualUnit()) {
	        		if(unit + b.getApplyUnit() + b.getVirtualUnit() > b.getUnit()){
	        			return Ret.fail("msg", "已超过最多可购买份数，请检查！"); 
	        		}
	    		}
	    		
	    		// 判断单笔最多可购买份数
	    		if(b.getMostUnit()>0 && unit>b.getMostUnit()) {
	        		return Ret.fail("msg", "已超过单笔最多可购买份数，请检查！"); 
	    		}
	
	    		// 判断新手标只能购买一次
	    		if(Str.eq("first", b.getType()) && trade.hashFirst(u.getId())){
	    			return Ret.fail("msg", "新手标只能投资一次！");
	    		}
				
	    		// 创建交易
				Trade t = new Trade();
	    		
	    		// 判断是否是体验标
	    		if(b.isExperi()){
	    			// 判断体验金
	    			CouponLog c = CouponLog.me.findById(tyjId);
	    			if(tyjId > 0){
	    				if(c!=null && Str.eq("tyj", c.getType()) && Str.eq("normal", c.getStr("status"))){
	    					if(StringUtils.contains(c.getAllowBid(), ","+b.getType()+",")){
	    						
	    						// 判断优惠券金额
	    						if(c.getAmount() + b.getApplyUnit() + b.getVirtualUnit() > b.getUnit()){
	    		        			return Ret.fail("msg", "优惠券已超过最多可购买金额，请检查！");
	    						}
	    						if(Money.sub(amount,Double.valueOf(c.getLimitAmount()))>=0){
	    							
	    							//重置购买金额和份数
	    							amount = c.getAmount();
	    							unit = (int) amount;
	    							double real_amount = Money.safeSub(amount, c.getAmount());
	    							t.setBidId(b.getId());
	    							t.setUserId(u.getId());
	    							t.setAmount((int)amount);
	    							t.setCreateTime(now);
	    							t.setUnit(unit);
	    							t.setBidName(b.getName());
	    							t.setCode(DateUtil.getTimeStamp());
	    							t.setStatus(BidStatus.TRADING);
	    							t.setTyj(tyjId);
	    							t.setJxq(0);
	    							t.setTzhb(0);
	    	        				t.setType(b.getType());
	    							t.setTargetAmount(0.0);
	    							t.setDayLimit(b.getDayLimit());
	    	        				t.setRealName(u.getRealName());
	    	        				t.setMobile(u.getMobile());
	    							t.save();
	    							
	    							// 自动结束交易
	    							isSuccess = true;
	    							tradeAmount = real_amount;
	    							
	    						}else{
	    		        			return Ret.fail("msg", "不可小于体验金单笔投资最低限额！");
	    						}
	    					}else{
			        			return Ret.fail("msg", "体验金不适用于此标的！");
	    					}
	    				}else{
		        			return Ret.fail("msg", "体验金不存在或已被使用！");
	    				}
	    			}
	    		}else{
	    			// 判断加息券
	    			CouponLog c = CouponLog.me.findById(jxqId);
	    			if(jxqId > 0){
	    				if(c!=null && Str.eq("jxq", c.getType()) && Str.eq("normal", c.getStr("status"))){
	    					if((Str.ne("public", b.getType()) && StringUtils.contains(c.getAllowBid(), ","+b.getType()+",")) 
	    							|| StringUtils.contains(c.getAllowBid(), ","+b.getDayLimit()+"day,")){
	        					if(Money.sub(amount,Double.valueOf(c.getLimitAmount()))<0){
	    		        			return Ret.fail("msg", "加息券不可小于加息券单笔投资最低限额！");
	    						}
	    					}else{
			        			return Ret.fail("msg", "加息券不适用于此标的！");
	    					}
	    				}else{
		        			return Ret.fail("msg", "加息券不存在或已被使用！");
	    				}
	    			}
	    			
	    			// 判断投资红包
	    			c = CouponLog.me.findById(tzhbId);
	    			if(tzhbId>0){
	    				if(c!=null && Str.eq("tzhb", c.getType()) && Str.eq("normal", c.getStr("status"))){
	    					if((Str.ne("public", b.getType()) && StringUtils.contains(c.getAllowBid(), ","+b.getType()+","))  || StringUtils.contains(c.getAllowBid(), ","+b.getDayLimit()+"day,")){
	        					if(Money.sub(amount,Double.valueOf(c.getLimitAmount()))<0){
	    		        			return Ret.fail("msg", "不可小于投资红包单笔投资最低限额！");
	    						}
	    					}else{
			        			return Ret.fail("msg", "投资红包不适用于此标的！");
	    					}
	    				}else{
		        			return Ret.fail("msg", "投资红包不存在或已被使用！");
	    				}
	    			}
	    			
	    			// 收投资金 
	    			User fund = User.me.findById(b.getFund());
	    			XmlHelper res = Gateway.freezePay(u.getCode(), fund.getCode(), amount, u.getRealName()+"投资"+b.getName()+""+amount+"元",false);
	        		// 创建交易流水号
	    			tradeCode = res.getCode();
	    			logger.error("金额==="+amount+"  用户==="+u.getCode()+" 标的CODE==="+b.getCode());
	        		
	    			if(res.isSuccess()){
	    				
	    				t.setBidId(b.getId());
	    				t.setUserId(u.getId());
	    				t.setAmount((int)amount);
	    				t.setCreateTime(now);
	    				t.setUnit(unit);
	    				t.setBidName(b.getName());
	    				t.setCode(tradeCode);
						t.setStatus(BidStatus.TRADING);
	    				t.setTyj(0);
	    				t.setJxq(0);
	    				t.setTzhb(0);
	    				t.setTargetAmount(0.0);
	    				t.setDayLimit(b.getDayLimit());
	    				t.setType(b.getType());
	    				t.setRealName(u.getRealName());
	    				t.setMobile(u.getMobile());
	    				t.save();
	
	    				// 自动结束交易
						isSuccess = true;
						tradeAmount = amount;
	    				// finish(t.getId(), amount);
	
						// 日志记录
						log.build("投资", "用户投资"+b.getName()+"成功", u.getId(), "user");
						
						// 插入财务记录
						FinanceLogService.build(u, fund, tradeAmount, tradeCode, FinanceLogType.INVEST, u.getRealName()+"投资"+b.getName()+amount+"元");
	    				
	    			} else {
	        			return Ret.fail("msg", res.getErrorMsg());
	    			} 
	    		}
	    		
	    		if(isSuccess && t!=null){
	    			
	        		// 本地化日期
	        		String localDate = DateUtil.format(DateUtil.DATE_LOCAL_FORMAT);
	        		
	        		// 更新投资红包
	        		CouponLog c = CouponLog.me.findById(tzhbId);
	        		if(c!=null){
	        			XmlHelper res = Gateway.pay(CONST.PLAT_CODE, u.getCode(), c.getAmount(), "红包奖励",true,false);
	        			if(res.isSuccess()) {
	        				// 更新优惠券记录信息
	        				String title = c.getName();
	        				c.setTradeId(t.getId()).set("status", "used").setApplyTime(now).setSummary("投资"+b.getName()+"标的"+t.getAmount()+"元").update();
	        				t.setTzhb(tzhbId).update();
	        				
	        				// 插入红包使用记录
	        				InvestLogService.build("红包",InvestLogType.TZHB, t.getId(), c.getAmount(), u.getId(), "success", b.getName(), res.getCode());
	        				
	        				// 插入资产记录-已使用投资红包
	        				AssetsNotify notify = AssetsNotify.COUPON_USE; 
	        				AgentNotify agentNotify = AgentNotify.COUPON_USE;
	        				assetsLog.bulid(u, Str.format(notify.getName(), title), Str.format(notify.getContents(), localDate, b.getName(), title), Str.format(agentNotify.getContents(), u.getMobile()+"（"+u.getRealName()+"）", localDate, b.getName(), title, u.getAvailableAmount()));
	  				
	        				// 奖励到帐  
	        				AssetsNotify assets = AssetsNotify.COUPON_REPAY;
	        				agentNotify = AgentNotify.COUPON_REPAY;
	        				assetsLog.bulid(u, Str.format(assets.getName(), title), Str.format(assets.getContents(), title, c.getAmount()), Str.format(agentNotify.getContents(), u.getMobile()+"（"+u.getRealName()+"）", title, c.getAmount()));
	
	        				// 财务记录
	        				// FinanceLogService.build(User.me.findById(1), u, c.getAmount(), tzhbCode, FinanceLogType.TZHB, u.getRealName()+"投资【"+b.getName()+"】获得返现券分润"+c.getAmount()+"元");
	        			}
	        		}	
	        		
	        		// 更新加息券
	        		c = CouponLog.me.findById(jxqId);
	        		if(c!=null){
	        			String title = c.getAmount()+"%加息券";
	        			c.setTradeId(t.getId()).set("status", "used").setApplyTime(now).setSummary("投资"+b.getName()+"标的"+t.getAmount()+"元").update();
	    				t.setJxq(jxqId).update();
	        			
	        			// 插入资产记录-加息券
	        			AssetsNotify notify = AssetsNotify.COUPON_USE; 
	        			AgentNotify agentNotify = AgentNotify.COUPON_USE;
	        			assetsLog.bulid(u, Str.format(notify.getName(), title), Str.format(notify.getContents(), localDate, b.getName(), title), Str.format(agentNotify.getContents(), u.getMobile()+"（"+u.getRealName()+"）", localDate, b.getName(), title));
	        		}
	        		
	        		// 更新体验金
	        		c = CouponLog.me.findById(tyjId);
	        		if(c!=null){
	        			String title = c.getAmount()+"元体验金";
	        			c.setTradeId(t.getId()).set("status", "used").setApplyTime(now).setSummary("投资"+b.getName()+"标的"+t.getAmount()+"元").update();
	        			
	        			// 插入资产记录-体验金
	        			AssetsNotify notify = AssetsNotify.COUPON_USE; 
	        			AgentNotify agentNotify = AgentNotify.COUPON_USE;
	        			assetsLog.bulid(u, Str.format(notify.getName(), title), Str.format(notify.getContents(), localDate, b.getName(), title), Str.format(agentNotify.getContents(), u.getMobile()+"（"+u.getRealName()+"）", localDate, b.getName(), title));
	        		}
	        		
	        		logger.error("标的ID="+b.getId()+"   标的名称="+b.getStr("name")+"   标的类型="+b.getStr("type")+"   交易用户="+u.getStr("real_name")+"   交易ID="+t.getId()+"   本金="+amount);
	        		
	        		// 更新已购买数量 
	        		b.setApplyUnit(b.getApplyUnit() + t.getInt("unit")); 
	        		
	        		// 判断是否满标
	        		b.setJoinNum(trade.userCount(b));
	        		if(b.getApplyUnit() + b.getVirtualUnit() >=b.getUnit()){
	        			b.setStatus(BidStatus.FULL);
	        		}
	        		b.update();
	        		  
	        		// 更新交易状态
	        		t.setStatus(BidStatus.TRADING).update();
	        	
	        		// 插入交易记录
	        		InvestLogService.build("投资", InvestLogType.INVEST, t.getId(), amount, u.getId(), "success", b.getName(), tradeCode);
	        		
	        		// 插入资产记录-交易成功
	        		String date = DateUtil.format(t.getCreateTime(), DateUtil.DATE_LOCAL_FORMAT);
	        		AssetsNotify notify = AssetsNotify.TRADE_SUCCESS;
	        		AgentNotify agentNotify = AgentNotify.TRADE_SUCCESS;
	        		assetsLog.bulid(u, notify.getName(), Str.format(notify.getContents(), date, b.getName(), t.getAmount(), CONST.find("PLAT_PHONE")),Str.format(agentNotify.getContents(), u.getMobile()+"（"+u.getRealName()+"）", date, b.getName(), t.getAmount(), u.getAvailableAmount()));
	
	        		// 获取优惠券
	        		coupon.available(u.getId(), CouponRule.TRADE, tradeAmount, t.getId()); 	
	
	        		// 更新用户是否已经交易
	        		if(u.getIsTraded()!=1) {
	            		u.setIsTraded(1).update();
	        		}
	    			return Ret.ok("msg", "投资成功！");
	    		}
	    	} catch(Exception e) {
	    		e.printStackTrace();
	    		logger.error(e);
				return Ret.fail("msg", "服务器繁忙，请重试！");
	    	}
			return Ret.fail("msg", "服务器繁忙，请重试！");
		}else{
			return Ret.fail("msg", "您仍未实名绑卡，不可进行投资");
		}
    }
    
	/**
	 * 获取用户第一次交易（除去体验标）
	 */ 
	public Trade findFirst(int userId) {
		return Trade.me.findFirst("select * from trade where tyj=0 and user_id=?",userId);
	}
 
	/**
	 * 判断用户是否购买过新手标
	 */
	public boolean hashFirst(int userId) {
		return Trade.me.findFirst("select t.id from trade t left join bid b on t.bid_id = b.id where b.type='first' and t.user_id=? ",userId)!=null;
	}
	
	/**
	 * 统计所有投资金额
	 */
	public double total() {
		Trade t = Trade.me.findFirst("select SUM(amount) as total from trade");
		return Money.get(t.getDouble("total"));
	}
	
	/**
	 * 统计所有投资金额
	 */
	public double totalExperi() {
		Trade t = Trade.me.findFirst("select SUM(amount) as total from trade where tyj>0");
		return Money.get(t.getDouble("total"));
	}

	/**
	 * 根据编码获取交易记录
	 */
	public Trade findByCode(String code) {
		return Trade.me.findFirst("select * from trade where code=?", code); 
	}

	/**
	 * 获取用户的交易记录
	 */ 
	public Page<Trade> findByUser(int page, int size, Integer userId, String status) {
		return this.findByUser(page, size, userId, status, "", "");
	}

	/**
	 * 获取用户的交易记录
	 */ 
	public Page<Trade> findByUser(int page, int size, Integer userId, String status, String start, String end) {
		String selectStr = "select t.id, t.bid_id, b.status,b.name, b.rate, b.end_time, b.invest_start_time, b.invest_end_time, b.actual_repay_time, b.type, t.amount, t.create_time, b.start_time, t.target_time, t.target_amount, t.target_plus_amount, t.target_jxq_amount, t.day_limit, b.plus_rate, b.virtual_unit, t.jxq, cast(FORMAT(t.target_amount+t.target_plus_amount+t.target_jxq_amount,2) as char(32)) as total ";
		String dateWhere = "";
		if(Str.nb(start)) {
			dateWhere += " and t.create_time>='"+start+"'";
		}
		if(Str.nb(end)) {
			dateWhere += " and t.create_time<='"+end+"'";
		}
		if(Str.eq("holding", status)){
			return Trade.me.paginate(page, size, selectStr, "from trade t left join bid b on t.bid_id=b.id where t.user_id=? and b.status='repaying' "+dateWhere+" order by b.invest_end_time asc", userId);
		}else if(Str.eq("trading", status)){  
			return Trade.me.paginate(page, size, selectStr, "from trade t left join bid b on t.bid_id=b.id where t.user_id=? and (b.status='trading' or b.status='full') "+dateWhere+" order by t.create_time desc", userId);
		}else if(Str.eq("all", status)){
			return Trade.me.paginate(page, size, selectStr, "from trade t left join bid b on t.bid_id=b.id where t.user_id=? "+dateWhere+" order by t.target_time desc", userId);
		}else{
			return Trade.me.paginate(page, size, selectStr, "from trade t left join bid b on t.bid_id=b.id where t.user_id=? AND b.status=? "+dateWhere+" order by t.target_time desc", userId, status);
		}
	}
	
    /**
     * 满标机制触发
     */
	public synchronized static Ret full(Bid b) throws Exception {
		
		// 判断标的状态
		if(Str.eq(BidStatus.FULL, b.getStatus())) {
			
			Date now = new Date(); 
			boolean isExperiBid = Str.eq("experi", b.getType());
			
			// 获取标的检查对象
			BidCheck bc = bidCheck.findByBid(b.getId());

			// 借款人
			User fund = User.me.findById(b.getFund());
			System.out.println(fund.getCode());
			
			// 是否是平台加息
			int isPlatPlus = b.getPlusFund()==1 ? 1 : 0;

			// 计算真实投资金额
			double realTradeAmount = trade.totalByBid(b.getId());
			
			// 是否提前付息
			boolean isInterestAdvance = b.getInterestAdvance()==1;
			
			// 判断是否满标
			if(b.getApplyUnit() + b.getVirtualUnit() >=b.getUnit()){
				
				// 至少有一笔真实投资
				if(b.getApplyUnit()>0 && realTradeAmount>0) {
					
					// 开始满标放款 
					if(!isExperiBid && bc.getIsPayFund()!=1){
						String summary = "项目"+b.getName()+"满标放款";
						XmlHelper res = Gateway.unFreeze(fund.getCode(), realTradeAmount, summary);
						if(res.isSuccess()){
							bc.setIsPayFund(1).update();
						}else{
							logger.error("放款失败！原因："+res.getErrorMsg());
							return Ret.fail("msg", "放款失败！原因："+res.getErrorMsg());
						}
					}
					
					// 获取分润模板信息
					ShareBenefitTemplet sbt = ShareBenefitTemplet.me.findById(b.getSbtId());
					
					// 借款机构支付平台分润到平台
					if(!isExperiBid && sbt.getPlatRate()>0 && bc.getIsPayPlat()!=1){
						double platBenefitAmount = Money.format(Money.div(Money.mul(realTradeAmount, sbt.getPlatRate()), 100));
						logger.error("plat_benefit_amount="+Money.format(platBenefitAmount)+"  借款机构="+fund.getCode());
						XmlHelper res = Gateway.pay(fund.getCode(), CONST.PLAT_CODE, platBenefitAmount, "项目"+b.getName()+"平台手续费",true,false);
						if(res.isSuccess()){
							bc.setIsPayPlat(1).update();
			    			FinanceLogService.build(fund, User.me.findById(4), platBenefitAmount, res.getCode(), FinanceLogType.PLAT_BENEFIT, "项目"+b.getName()+"平台手续费");
						}else{
							logger.error("标的ID="+b.getId()+"   标的名称："+b.getStr("name")+" 代扣手续费给平台失败！原因："+res.getErrorMsg());	
							return Ret.fail("msg", "标的ID："+b.getId()+"   标的名称："+b.getStr("name")+" 代扣手续费给平台失败！原因："+res.getErrorMsg());
						}
					}
					
					// 开始逐个判断标的的每一笔交易
					for(Trade td : trade.findByBid(b.getId())){
						
						// 判断是否是体验标
						boolean isExperi = td.getTyj()>0;
						
						// 获取投资用户
						User u = User.me.findById(td.getUserId());
						
						// 开始生成更新还款计划
						String repayType = b.getRepayType();
						
						// 获取投资人信息
						User invester = User.me.findById(td.getUserId());
						
						 // double principle = Money.safeSub(td.getAmount(), td.getTyj()>0 ? CouponLog.me.findById(td.getTyj()).getAmount() : 0); 
						double principle = td.getAmount();
						double rate = Money.div(b.getRate(), 100);
						double plus_rate = Money.div(b.getPlusRate(), 100);
						double jxq_rate = Money.div(td.getJxq()>0?CouponLog.me.findById(td.getJxq()).getAmount() : 0, 100);
						// double jxq_rate = Money.div(td.getJxq()>0?CouponLog.me.findById(td.getJxq()).limitAmount(td.getAmount()) : 0, 100);
						double amount = 0;

						if(!isExperiBid){
							
							logger.error("用户="+td.getUserId()+" 购买金额"+td.getAmount()+"元");
							// 获取投资人的经纪人
							User agent = User.me.findById(invester.getAgentId());
							if(agent!=null && agent.getIsAgent()==1 && sbt.getAgentRate()>0){
								trade.payBenefit(b, td, u, fund, agent, sbt.getAgentRate());
							}
							
							/** 更新推荐人分润
							UserReferee ur = userReferee.findByUser(u.getId());
							if(ur!=null) {
								ur.setAmount(Money.format(Money.mul(Money.div(Money.mul(td.getAmount(), Money.div(b.getRate(),100)), 3600),b.getDayLimit()))).update();
							}*/
						} 
						
						if(Str.eq(RepayType.ALL, repayType)){	//到期归还本金及利息
							
							logger.error("到期归还本金及利息");
							double intertestAll = 0.00;
							double targetAmount = 0.00;
							double targetPlusAmount = 0.00;
							double targetJxqAmount = 0.00;
							double intertest = Money.format(Money.mul(principle, 1 + Money.mul(Money.div(rate, 360), b.getDayLimit()))-principle,RoundingMode.DOWN);
							double intertest_plus = Money.format(Money.mul(principle, 1 + Money.mul(Money.div(plus_rate, 360), b.getDayLimit()))-principle,RoundingMode.DOWN);
							double intertest_jxq = Money.format(Money.mul(principle, 1 + Money.mul(Money.div(jxq_rate, 360), b.getDayLimit()))-principle,RoundingMode.DOWN);
							intertestAll += intertest;
							
							// 体验标需要将本金置零
							amount = isExperi ? 0 : principle;
							
							Repay rp = new Repay();
							rp.setBidName(b.getName());
							rp.setTradeId(td.getId());
							rp.setCreateTime(now);
							rp.setStatus("process");
							rp.setUserId(u.getId());
							rp.setRepayUserId(fund.getId());
							rp.setRepayRealName(fund.getRealName());
							rp.setPeriod(1);
							rp.setApplyPeriod(1);
							rp.setType(RepayType.ALL);
							rp.setAmount(amount);
							rp.setInterest(isInterestAdvance?0.00:intertest);
							rp.setPlus(intertest_plus);
							rp.setJxq(intertest_jxq);
							rp.setPeriodTotal(Money.format(rp.getAmount() + rp.getInterest() + rp.getPlus() + rp.getJxq()));
							rp.setTotal(rp.getPeriodTotal());
							String repayTime = DateUtil.adjust(now, b.getDayLimit(), DateUtil.DATE_DEFAULT_FORMAT)+" 23:59:59";
							rp.setRepayTime(DateUtil.parse(repayTime)); 
							rp.setIsLast(1);
							rp.setIsPlatPlus(isPlatPlus);
							rp.setBidId(b.getId());
							rp.setMobile(u.getMobile());
							rp.setRealName(u.getRealName());
							rp.setIsPlatPay(0);
							rp.setIsFundPay(0);
							rp.save();

							targetAmount += rp.getInterest();
							targetPlusAmount += rp.getPlus();
							targetJxqAmount += rp.getJxq();
							
							// 更新还款时间 & 放款时间 & 还款总金额
							td.setStatus(BidStatus.REPAYING)
								.setTargetAmount(targetAmount)
								.setTargetPlusAmount(targetPlusAmount)
								.setTargetJxqAmount(targetJxqAmount)
								.setApplyTime(now)
								.setTargetTime(rp.getRepayTime())
								.update();

							// 判断是否需要提前付息
							if(isInterestAdvance) {
								trade.payInterestAdvance(td, u, fund, intertestAll);
							}
							
						}else if(Str.eq(RepayType.SCHEDULE, repayType)){ // 按月还息，到期还本
							logger.error("按月还息，到期还本");

							int month_limit = Integer.valueOf(b.getDayLimit()/30);

							double total = 0.00;
							double intertestAll = 0.00;
							double targetAmount = 0.00;
							double targetPlusAmount = 0.00;
							double targetJxqAmount = 0.00;
							Date last = new Date();
							List<Repay> repays = new ArrayList<Repay>();
							double intertest = Money.div(Money.mul(Money.div(Money.mul(principle, rate), 360), b.getDayLimit()), Double.valueOf(month_limit));
							double intertest_plus = Money.div(Money.mul(Money.div(Money.mul(principle, plus_rate), 360), b.getDayLimit()), Double.valueOf(month_limit));
							double intertest_jxq = Money.div(Money.mul(Money.div(Money.mul(principle, jxq_rate), 360), b.getDayLimit()), Double.valueOf(month_limit));
							
							for(int i=1; i<=month_limit; i++){

								// 体验标需要将本金置零
								amount = isExperi ? 0 : principle;
								intertestAll += intertest;
								Repay rp = new Repay();
								rp.setBidName(b.getName());
								rp.setTradeId(td.getId());
								rp.setCreateTime(now);
								rp.setStatus("process");
								rp.setUserId(u.getId());
								rp.setRepayUserId(fund.getId());
								rp.setRepayRealName(fund.getRealName());
								rp.setPeriod(month_limit);
								rp.setApplyPeriod(i);
								rp.setType(RepayType.SCHEDULE);
								rp.setAmount(month_limit==i && !isExperi ? amount : 0.0);
								rp.setInterest(Money.format(isInterestAdvance?0.00:intertest,RoundingMode.DOWN));
								rp.setPlus(Money.format(intertest_plus,RoundingMode.DOWN));
								rp.setJxq(Money.format(intertest_jxq,RoundingMode.DOWN));
								rp.setPeriodTotal(rp.getAmount() + rp.getInterest() + rp.getPlus() + rp.getJxq());
								total = Money.add(total, rp.getPeriodTotal());
								rp.setIsLast(i==month_limit ? 1 : 0);
								rp.setIsPlatPlus(isPlatPlus);
								String repayTime = DateUtil.adjust(now, 30 * i, DateUtil.DATE_DEFAULT_FORMAT)+" 23:59:59";
								rp.setRepayTime(DateUtil.parse(repayTime));
								rp.setBidId(b.getId());
								rp.setMobile(u.getMobile());
								rp.setRealName(u.getRealName());
								rp.setIsPlatPay(0);
								rp.setIsFundPay(0);
								rp.save();
								repays.add(rp);

								targetAmount += rp.getInterest();
								targetPlusAmount += rp.getPlus();
								targetJxqAmount += rp.getJxq();
								last = rp.getRepayTime();
								
							}
							
							// 更新还款状态 & 还款时间 & 放款时间 & 还款总金额
							td.setStatus(BidStatus.REPAYING)
								.setTargetAmount(targetAmount)
								.setTargetPlusAmount(targetPlusAmount)
								.setTargetJxqAmount(targetJxqAmount)
								.setApplyTime(now)
								.setTargetTime(last)
								.update();


							// 判断是否需要提前付息
							if(isInterestAdvance) {
								trade.payInterestAdvance(td, u, fund, intertestAll);
							}
							
							// 刷新总额
							for(Repay rp : repays) {
								rp.setTotal(Money.format(total)).update();
							}
							
						}else if(Str.eq(RepayType.INTEREST, repayType)){ // 按月等额本息		
							logger.error("按月等额本息");				

							double total = 0.00;
							double intertestAll = 0.00;
							double targetAmount = 0.0;
							double targetPlusAmount = 0.00;
							double targetJxqAmount = 0.00;
							Date last = new Date();
							List<Repay> repays = new ArrayList<Repay>();
							int month_limit = Integer.valueOf(b.getDayLimit()/30);
					        Map<Integer, BigDecimal> mapPrincipal = AverageCapitalPlusInterestUtils.getPerMonthPrincipal(principle, rate, month_limit);
					        Map<Integer, BigDecimal> mapInterest = AverageCapitalPlusInterestUtils.getPerMonthInterest(principle, rate, month_limit);
					        Map<Integer, BigDecimal> mapInterestPlus = AverageCapitalPlusInterestUtils.getPerMonthInterest(principle, plus_rate, month_limit);
					        Map<Integer, BigDecimal> mapInterestJxq = AverageCapitalPlusInterestUtils.getPerMonthInterest(principle, jxq_rate, month_limit);
							
					        for(int i=1;i<=month_limit;i++){
					        	
								logger.error("本金==="+mapPrincipal.get(i).doubleValue()+"===利息==="+mapInterest.get(i));

								// 体验标需要将本金置零
								amount = isExperi ? 0 : mapPrincipal.get(i).doubleValue();
								intertestAll += mapInterest.get(i).doubleValue();
								Repay rp = new Repay();
								rp.setBidName(b.getName());
								rp.setTradeId(td.getId());
								rp.setCreateTime(now);
								rp.setStatus("process");
								rp.setUserId(u.getId());
								rp.setRepayUserId(fund.getId());
								rp.setRepayRealName(fund.getRealName());
								rp.setPeriod(month_limit);
								rp.setApplyPeriod(i);
								rp.setType(RepayType.INTEREST);
								rp.setAmount(amount);
								rp.setInterest(isInterestAdvance?0.00:mapInterest.get(i).doubleValue());
								// rp.setInterest(mapInterest.get(i).doubleValue());
								rp.setPlus(mapInterestPlus.get(i).doubleValue());
								rp.setJxq(mapInterestJxq.get(i).doubleValue());
								rp.setPeriodTotal(Money.format(amount + mapInterest.get(i).doubleValue() + mapInterestPlus.get(i).doubleValue() + mapInterestJxq.get(i).doubleValue()));
								total = Money.add(total, rp.getPeriodTotal());
								String repayTime = DateUtil.adjust(now, 30 * i, DateUtil.DATE_DEFAULT_FORMAT)+" 23:59:59";
								rp.setRepayTime(DateUtil.parse(repayTime));
								rp.setIsLast(i==month_limit ? 1 : 0);
								rp.setIsPlatPlus(isPlatPlus);
								rp.setBidId(b.getId());
								rp.setMobile(u.getMobile());
								rp.setRealName(u.getRealName());
								rp.setIsPlatPay(0);
								rp.setIsFundPay(0);
								rp.save();
								repays.add(rp);

								targetAmount += rp.getInterest();
								targetPlusAmount += rp.getPlus();
								targetJxqAmount += rp.getJxq();
								last = rp.getRepayTime();
							}
							
							// 更新还款时间 & 放款时间 & 还款总金额
							td.setStatus(BidStatus.REPAYING)
								.setTargetAmount(targetAmount)
								.setTargetPlusAmount(targetPlusAmount)
								.setTargetJxqAmount(targetJxqAmount)
								.setApplyTime(now)
								.setTargetTime(last)
								.update();


							// 判断是否需要提前付息
							if(isInterestAdvance) {
								trade.payInterestAdvance(td, u, fund, intertestAll);
							}
							
							// 刷新总额
							for(Repay rp : repays) {
								rp.setTotal(Money.format(total)).update();
							}
							
						}else if(Str.eq(RepayType.CAPITAL, repayType)){ // 按月等额本息		
							logger.error("按月等额本金");		

							double total = 0.00;
							double intertestAll = 0.00;
							double targetAmount = 0.0;
							double targetPlusAmount = 0.00;
							double targetJxqAmount = 0.00;
							Date last = new Date();
							List<Repay> repays = new ArrayList<Repay>();
							int month_limit = Integer.valueOf(b.getDayLimit()/30);
							Map<Integer, Double> mapPrincipal = AverageCapitalUtils.getPerMonthPrincipal(principle, month_limit);
					        Map<Integer, Double> mapInterest = AverageCapitalUtils.getPerMonthInterest(principle, rate, month_limit);
					        Map<Integer, Double> mapInterestPlus = AverageCapitalUtils.getPerMonthInterest(principle, plus_rate, month_limit);
					        Map<Integer, Double> mapInterestJxq = AverageCapitalUtils.getPerMonthInterest(principle, jxq_rate, month_limit);

							logger.error("mapPrincipal==="+mapPrincipal);
							logger.error("mapInterest==="+mapInterest);
							logger.error("mapInterestPlus==="+mapInterestPlus);
							logger.error("mapInterestJxq==="+mapInterestJxq);
							
					        for(int i=1;i<=month_limit;i++){

								// 体验标需要将本金置零
								amount = isExperi ? 0 : mapPrincipal.get(i).doubleValue();
								intertestAll += mapInterest.get(i).doubleValue();
								Repay rp = new Repay();
								rp.setBidName(b.getName());
								rp.setTradeId(td.getId());
								rp.setCreateTime(now);
								rp.setStatus("process");
								rp.setUserId(u.getId());
								rp.setRepayUserId(fund.getId());
								rp.setRepayRealName(fund.getRealName());
								rp.setPeriod(month_limit);
								rp.setApplyPeriod(i);
								rp.setType(RepayType.CAPITAL);
								rp.setAmount(amount);
								rp.setInterest(isInterestAdvance?0.00:mapInterest.get(i).doubleValue());
								rp.setPlus(mapInterestPlus.get(i).doubleValue());
								rp.setJxq(mapInterestJxq.get(i).doubleValue());
								rp.setPeriodTotal(Money.format(rp.getAmount() + mapInterest.get(i).doubleValue() + mapInterestPlus.get(i).doubleValue() + mapInterestJxq.get(i).doubleValue()));
								total = Money.add(total, rp.getPeriodTotal());
								String repayTime = DateUtil.adjust(now, 30 * i, DateUtil.DATE_DEFAULT_FORMAT)+" 23:59:59";
								rp.setRepayTime(DateUtil.parse(repayTime));
								rp.setIsLast(i==month_limit ? 1 : 0);
								rp.setIsPlatPlus(isPlatPlus);
								rp.setBidId(b.getId());
								rp.setMobile(u.getMobile());
								rp.setRealName(u.getRealName());
								rp.setIsPlatPay(0);
								rp.setIsFundPay(0);
								rp.save();
								repays.add(rp);

								targetAmount += rp.getInterest();
								targetPlusAmount += rp.getPlus();
								targetJxqAmount += rp.getJxq();
								last = rp.getRepayTime();
							}
							
							// 更新还款时间 & 放款时间 & 还款总金额
							td.setStatus(BidStatus.REPAYING)
								.setTargetAmount(targetAmount)
								.setTargetPlusAmount(targetPlusAmount)
								.setTargetJxqAmount(targetJxqAmount)
								.setApplyTime(now)
								.setTargetTime(last)
								.update();


							// 判断是否需要提前付息
							if(isInterestAdvance) {
								trade.payInterestAdvance(td, u, fund, intertestAll);
							}
							
							// 刷新总额
							for(Repay rp : repays) {
								rp.setTotal(Money.format(total)).update();
							}
						}
					}
					
					// 修改标的状态，进入还款中
					logger.error("满标，修改标的状态，进入还款中");	
					String investEndTime = DateUtil.adjust(now, b.getDayLimit(), DateUtil.DATE_DEFAULT_FORMAT)+" 23:59:59";
					b.setRecommand(0)
						.set("status", BidStatus.REPAYING)
						.setInvestStartTime(now)
						.setInvestEndTime(DateUtil.parse(investEndTime))
						.update();
					bc.setIsPayAgent(1).update();
					logger.error("放款成功！");	
					return Ret.ok("msg", "放款成功！");
				} else {
					// 全部虚增的情况
					
					// 更新放款标志
					bc.setIsPayFund(1).setIsPayPlat(1).setIsPayAgent(1).setIsPayFriend(1).update();
					
					// 更新标的状态
					String investEndTime = DateUtil.adjust(now, b.getDayLimit(), DateUtil.DATE_DEFAULT_FORMAT)+" 23:59:59";
					b.setAllVirtual(1).setRecommand(0).set("status", BidStatus.REPAYING).setInvestStartTime(now).setInvestEndTime(DateUtil.parse(investEndTime)).update();
					
					return Ret.ok("msg", "放款成功！（全虚增）");
				}
			}else{
				return Ret.fail("msg", "标的数量不可大于最大份数！"); 
			}
		}else{
			return Ret.fail("msg", "标的状态不正确！"); 
		}
	}
	
	/**
	 * 提前付息
	 */
	private void payInterestAdvance(Trade t, User u, User fund, Double amt) throws Exception {
		logger.error("提前付息==="+u.getId()+"==="+fund.getId());
		XmlHelper res = Gateway.pay(fund.getCode(), u.getCode(), Money.format(amt),"提前付息到用户"+amt);
		String interestAdvanceCode = res.getStr("mchnt_txn_ssn");
		
		if(res.isSuccess()){								
			// String json = JSON.toJSONString(Data.put("bid_name",b.getName(), "rate",String.valueOf(b.getRate()), "jxq_rate","0.00", "plus_rate","0.00", "period","1", "apply_period","1", "amount","0.00", "interest",String.valueOf(amt), "plus","0.00", "jxq","0.00")); 
			InvestLogService.build("回款", InvestLogType.REPAY, t.getId(), amt, u.getId(), "success", "提前付息", interestAdvanceCode);
		}else{
			logger.error("借款机构支付提前付息到用户失败，错误信息："+res.getErrorMsg());
		}
	}
	
	/**
	 * 支付经纪人分润
	 */
	private void payBenefit(Bid b, Trade td, User u, User fund, User agent, double rate) throws Exception {

		// 备注内容
		String remark = "项目"+b.getName()+"的经纪人分润";
		// 计算分润金额
		double userBenefitAmount = Money.format(Money.mul(Money.div(Money.mul(td.getAmount(), Money.div(rate,100)), 360),b.getDayLimit()));
		// 借款机构支付经纪人分润到经纪人
		XmlHelper res = Gateway.pay(fund.getCode(), agent.getCode(), Money.format(userBenefitAmount),remark);
		String agentShareCode = res.getStr("mchnt_txn_ssn");
		
		if(res.isSuccess()) {
				
			// 插入交易记录明细
			InvestLogService.build("分润", InvestLogType.SHARE, u.getId(), userBenefitAmount, agent.getId(), "success", b.getName(), agentShareCode);
			
			// 插入财务记录
			FinanceLogService.build(fund, agent, userBenefitAmount, agentShareCode, FinanceLogType.AGENT_BENEFIT, remark);
			
			// 插入经纪人分润记录
			ShareBenefit sb = new ShareBenefit();
			sb.setBidName(b.getName());
			sb.setUserId(u.getId());
			sb.setAgentId(u.getAgentId());
			sb.setRealName(u.getRealName());
			sb.setTradeId(td.getId());
			sb.setTradeAmount(td.getAmount());
			sb.setAmount(userBenefitAmount);
			sb.setCreateTime(new Date());  
			sb.setTradeTime(td.getCreateTime());
			sb.save();
			 
			// 发送经纪人分润记录提醒
			String date = DateUtil.format(td.getCreateTime(), DateUtil.DATE_LOCAL_FORMAT);
			AgentNotify agentNotify = AgentNotify.TRADE_BENEFIT;
			assetsLog.bulid(agent, agentNotify.getName(), Str.format(agentNotify.getContents(), u.getMobile(), date, b.getName(), td.getAmount()), Str.format(agentNotify.getContents(), u.getMobile()+"（"+u.getRealName()+"）", date, b.getName(), td.getAmount()));
		}else{
			logger.error("融资机构支付经纪人分润到用户失败，错误信息："+res.getErrorMsg());
		}
	}
	
	/**
	 * 分页数据
	 */ 
	public Page<Trade> list(int page, int size, Map<String, String> map) {		
		String orderBy = " order by create_time desc";
		if(Str.eq("repaying", map.get("status"))) {
			orderBy = " order by target_time asc";
		}else if(Str.eq("finish", map.get("status"))) {
			orderBy = " order by target_time desc";
		}
		return Trade.me.paginate(page, size, buildSql("trade",Arrays.asList("mobile", "real_name", "apply_time", "target_time", "bid_name", "bid_id", "create_time", "status", "user_id", "amount", "tyj"),map,orderBy));
	}

	/**
	 * 用户交易明细
	 */ 
	public Record findDetailByUser(int id) {
		return Db.findFirst("select b.id, b.unit_amount, b.repay_cycle, b.actual_repay_time, b.repay_type, b.least_unit, b.day_limit, b.status,b.name, b.rate, b.end_time, b.invest_start_time, b.invest_end_time, b.type, t.amount, b.start_time, t.target_amount, t.target_plus_amount, t.target_jxq_amount, b.plus_rate, b.virtual_unit, t.jxq, t.create_time from trade t LEFT JOIN bid b ON t.bid_id=b.id where t.id=?", id);
	}
	
	
	public List<Trade> findByBid(int bidId) {
		return Trade.me.find("select * from trade t where bid_id=?",bidId); 
	}
	
	/**
	 * 统计标的融资金额
	 */
	public double totalByBid(int bidId) {
		BigDecimal bd = Db.findFirst("select sum(amount) as total from trade where bid_id=?",bidId).get("total");
		return bd!=null ? bd.doubleValue() : 0.00;
	}

	public Page<Trade> findByAgent(int page, int size, String start, String end, String agents) {
		return Trade.me.paginate(page, size, "select *", "from trade WHERE tyj=0 and user_id in (select id from (select distinct id from user where agent_id in ("+agents+")) as tmp) AND apply_time>=? AND apply_time<=? ORDER BY create_time DESC ", start, end);
	}

	public long coutByBid(Bid b) {
		Record r = Db.findFirst("select count(id) as total from trade where bid_id=?",b.getId()); 
		return r!=null ? r.getLong("total") : 0;
	}

	public double sumByParams(Map<String, String> params) {
		BigDecimal bd = Db.findFirst(buildSql("select sum(amount) as total ", "trade",Arrays.asList("bid_name", "create_time", "target_time", "amount", "status", "type"),params)).get("total");
		return bd!=null ? bd.doubleValue() : 0.00;
	}

	public long countByParams(Map<String, String> params) {
		Record r = Db.findFirst(buildSql("select count(id) as count ", "trade",Arrays.asList("bid_name", "create_time", "target_time", "amount", "status", "type"),params," group by user_id"));
		return r!=null ? r.getLong("count") : 0;
	}

	public int userCount(Bid b) {
		List<Record> r1 = Db.find("select id as total from trade where bid_id=? group by user_id",b.getId()); 
		List<Record> r2= Db.find("select id as total from bid_virtual_log where bid_id=? group by mobile",b.getId()); 
		return r1.size()+r2.size();
	}
}
